#include "TFrame.h"
#include <signal.h>

/*********** function implementation *************/
void WorkQueue::setNumThreads(int number) {// set the number of threads to the number //
  num_threads = number;
}

void* run_thread (void* x) {
  WorkQueue* Q = (WorkQueue*)x;
  while (true) {
#if 0
      cpu_set_t cpuset; // get cpu set
      CPU_ZERO(&cpuset);  // clear cpu set
      int s = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
      for (int j = 0 ; j < CPU_SETSIZE ; j++) {
          if (CPU_ISSET(j, &cpuset))
              printf("--->Thread %d running on processor %d\n", pthread_self(), j);
      }
#endif
      pthread_mutex_lock(&Q->qmutex); // acquire the mutex on the queue //
      while (Q->workq.empty()) {// queue is empty wait for it to be filled 
          // wait on conditional variables //
          pthread_mutex_lock(&Q->cmutex);
          Q->count++;
          if(Q->count == Q->num_threads) {
              pthread_cond_signal(&Q->done);
          }
          pthread_mutex_unlock(&Q->qmutex);
          pthread_cond_wait(&Q->empty_cond, &Q->cmutex);
          Q->count--;
          pthread_mutex_unlock(&Q->cmutex);
          pthread_mutex_lock(&Q->qmutex);
      }
      Task * myTask;
      myTask = Q->workq.front();
      if (myTask == NULL) {
          pthread_mutex_unlock(&Q->qmutex);
          continue;
      }
      Q->workq.pop();
      pthread_mutex_unlock(&Q->qmutex);
      myTask->run();
      myTask->broadcast();// pthread_cond_broadcast(&myTask->tdone);
      if (myTask != NULL) {
          //  myTask->lock_d();// pthread_mutex_lock(&myTask->dmutex);
          if(myTask->GetState()==Done) {
              //myTask->release_d();	
              myTask->DDone();

          } else {
              Q->addTask(myTask);
              //myTask->release_d();      
          }
          //pthread_mutex_unlock(&myTask->dmutex);
      }
      //printf("----> Thread %d leaving \n", pthread_self());
  }
  pthread_exit(NULL);  
}

int WorkQueue::initThreads() {
  if (!num_threads) {
    std::cout<<"You must initialize the number of threads!\n";
    return -1;
  }
  pool = new pthread_t[num_threads];
  for (int i=0; i < num_threads; i++) {
    pthread_create(&pool[i], NULL, run_thread, (void*)this);
  } 
  return 0;
}

int WorkQueue::addTask(Task* t) {
  t->SetState(Working);
//  pthread_mutex_init(&t->dmutex, NULL);
 // pthread_cond_init(&t->tdone, NULL);
  pthread_mutex_lock(&qmutex);
  if (workq.empty()) {
    workq.push(t);
    pthread_mutex_unlock(&qmutex);
    if (!pthread_cond_broadcast(&empty_cond)) {
      return 0;
    }else {
      return -1;
    }
  }
  workq.push(t);
  pthread_mutex_unlock(&qmutex);
  return 0;
}

int WorkQueue::wait_for_threads() {
  if (!num_threads)
    return -1;
  pthread_mutex_lock(&cmutex);
  pthread_mutex_lock(&qmutex);
  if (count == num_threads && workq.empty()) {
    pthread_mutex_unlock(&qmutex);
    pthread_mutex_unlock(&cmutex);
  //  printf("All threads are done!\n");
    delete[] pool; pool = NULL;
    return 0;
  } else {
    pthread_mutex_unlock(&qmutex);
    pthread_cond_wait(&done, &cmutex);
    pthread_mutex_unlock(&cmutex);
  //  printf("All threads are done!\n");
    delete[] pool; pool = NULL;
    return 0;
  }
}

void wait_for_task(Task* t) {// wait for a specific task to be done //
  t->lock_d();//pthread_mutex_lock(&t->dmutex);
  if (t->GetState()!=Done) {// not done yet //
  t->wait();//  pthread_cond_wait(&t->tdone, &t->dmutex);// wait for the signal to be done //
    t->release_d();//pthread_mutex_unlock(&t->dmutex);
    return;
  }else {
   t->release_d();// pthread_mutex_unlock(&t->dmutex); //simply leave //
    return;
  }
}

/******* End of function implementation ***********/
